import React, { useEffect, useRef } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import { PythonConsole } from '@metacell/geppetto-meta-ui/python-console/PythonConsole';

// Extend window interface
declare global {
  interface Window {
    GEPPETTO_CONFIGURATION?: {
      notebookName?: string;
      [key: string]: any;
    };
  }
}

interface ConsoleDictionary {
  [key: string]: string;
}

function compareConsoleDictionaries(dict1: ConsoleDictionary, dict2: ConsoleDictionary | null): ConsoleDictionary {
  const differences: ConsoleDictionary = {};

  if (!dict2) {
    return dict1; // in case dict2 has not yet been initialized then the array differences is the first element complete
  }

  const dict1Keys = Object.keys(dict1);
  const dict2Keys = Object.keys(dict2);
  if (dict1Keys.length !== dict2Keys.length) {
    differences.keysUnmatch = 'true';
  }

  for (const key of dict1Keys) {
    if (!dict2.hasOwnProperty(key)) {
      differences[key] = dict1[key];
    } else if (dict1[key] !== dict2[key]) {
      differences[key] = dict1[key];
    }
  }
  for (const key of dict2Keys) {
    if (!dict1.hasOwnProperty(key)) {
      differences[key] = dict2[key];
    } else if (dict2[key] !== dict1[key]) {
      differences[key] = dict2[key];
    }
  }

  return differences;
}

interface NetPyNEPythonConsoleProps {
  extensionLoaded: boolean;
  notebookVisible: boolean;
  updateConsole?: (data: { commands: ConsoleDictionary }) => void;
}

const NetPyNEPythonConsole: React.FC<NetPyNEPythonConsoleProps> = ({
  extensionLoaded,
  notebookVisible,
  updateConsole,
}) => {
  const containerRef = useRef<HTMLIFrameElement | null>(null);
  const focusNotebookValuesRef = useRef<ConsoleDictionary | null>(null);

  const getIFrameContent = (): ConsoleDictionary => {
    const container = containerRef.current || document.getElementById('pythonConsoleFrame') as HTMLIFrameElement;
    if (!container || !container.contentDocument) {
      return {};
    }

    const content = container.contentDocument;
    const cells = content.querySelectorAll('.CodeMirror-line');
    const currentNotebookValues: ConsoleDictionary = {};

    if (cells.length > 0) {
      cells.forEach((cell) => {
        const childrenCell = cell.children[0]?.children;
        if (!childrenCell) return;

        const routes: string[] = [];
        let val: string | undefined;

        for (let i = 0; i < childrenCell.length; i++) {
          const e = childrenCell[i] as HTMLElement;
          if (e.className === 'cm-string') {
            val = e.innerHTML;
          }
          if (e.className === 'cm-variable' || e.className === 'cm-property') {
            routes.push(e.innerHTML);
          }
        }

        if (routes?.length > 0 && val?.length > 0) {
          const property = routes.join('.');
          currentNotebookValues[property] = val;
        }
      });
    }
    return currentNotebookValues;
  };

  useEffect(() => {
    const pythonConsoleFrame = document.getElementById('pythonConsoleFrame');
    if (pythonConsoleFrame?.parentElement?.parentElement) {
      pythonConsoleFrame.parentElement.parentElement.style.display = 'block';
    }
  });

  useEffect(() => {
    if (notebookVisible && containerRef.current?.contentWindow) {
      const iframeWindow = containerRef.current.contentWindow;
      iframeWindow.onblur = handleIframeBlur;
      iframeWindow.onfocus = handleIframeFocus;
    }
  }, [notebookVisible]);

  const handleIframeBlur = (): void => {
    const notebookValues = getIFrameContent();
    const diff = compareConsoleDictionaries(focusNotebookValuesRef.current || {}, notebookValues);
    if (Object.keys(diff).length > 0 && updateConsole) {
      updateConsole({ commands: diff });
    }
  };

  const handleIframeFocus = (): void => {
    focusNotebookValuesRef.current = getIFrameContent();
  };

  useEffect(() => {
    const element = document.getElementById('pythonConsoleFrame');
    if (element) {
      containerRef.current = element as HTMLIFrameElement;
    }
  }, []);

  useEffect(() => {
    return () => {
      console.info('unmounting python console');
    };
  }, []);

  const notebookName = window.GEPPETTO_CONFIGURATION?.notebookName || 'notebook.ipynb';

  return (
    <ReactResizeDetector handleWidth handleHeight>
      {({ width, height }) => (
        <PythonConsole
          pythonNotebookPath={`notebooks/${notebookName}`}
          extensionLoaded={extensionLoaded}
          iframeHeight={height}
        />
      )}
    </ReactResizeDetector>
  );
};

export default NetPyNEPythonConsole;

